데이터 분석의 소개

데이터 분석이란 용어는 상당히 광범위한 용어이므로 여기에서는 통계적 분석과 머신 러닝이라는 두가지 세부 영역에 국한하여 데이터 분석을 설명하도록 한다.

데이터 분석이란

데이터 분석이란 어떤 데이터가 주어졌을 때

  • 데이터 간의 관계를 파악하거나
  • 파악된 관계를 사용하여 원하는 데이터를 만들어 내는 과정

으로 볼 수 있다.

데이터 분석의 유형

  • 예측(Prediction)
  • 클러스터링(Clustering)
  • 모사(Approximation)

데이터 분석의 유형은 다양하다. 그 중 널리 사용되는 전형적인 방법으로는 예측(prediction), 클러스터링(clustering), 모사(approximation) 등이 있다.

예측은 어떤 특정한 유형의 입력 데이터가 주어지면 데이터 분석의 결과로 다른 유형의 데이터가 출력될 수 있는 경우이다. 예를 들어 다음과 같은 작업은 예측이라고 할 수 있다.

  • 부동산의 위치, 주거환경, 건축연도 등이 주어지면 해당 부동산의 가치를 추정한다.
  • 꽃잎의 길이와 너비 등 식물의 외형적 특징이 주어지면 해당하는 식물의 종을 알아낸다.
  • 얼굴 사진이 주어지면 해당하는 사람의 이름을 출력한다.
  • 현재 바둑돌의 위치들이 주어지면 다음 바둑돌의 위치를 지정한다.

데이터 분석에서 말하는 예측이라는 용어는 시간상으로 미래의 의미는 포함하지 않는다. 시계열 분석에서는 시간상으로 미래의 데이터를 예측하는 경우가 있는데 이 경우에는 forecasting 이라는 용어를 사용한다.

클러스터링은 동일한 유형의 데이터가 주어졌을 때 유사한 데이터끼리 몇개의 집합으로 묶는 작업을 말한다. 예를 들어 다음과 같은 작업은 클러스터링이다.

  • 지리적으로 근처에 있는 지점들을 찾아낸다.
  • 유사한 단어를 포함하고 있는 문서의 집합을 만든다.
  • 유사한 상품을 구해한 고객 리스트를 생성한다.

모사는 대량의 데이터를 대표하는 소량의 데이터를 생성하는 작업이다.

  • 이미지나 음악 데이터를 압축한다.
  • 주식 시장의 움직임을 대표하는 지수 정보를 생성한다.

입력 데이터와 출력 데이터

만약 예측을 하고자 한다면 데이터의 유형을 입력 데이터와 출력 데이터라는 두 가지 유형의 데이터로 분류할 수 있어야 한다.

  • 입력 $X$
    • 분석의 기반이 되는 데이터
    • 독립변수 independent variable
    • feature, covariates, regressor, explanatory, attributes, stimulus
  • 출력 $Y$
    • 추정하거나 예측하고자 하는 데이터
    • 종속변수 dependent variable
    • target, response, regressand, label, tag

예측 작업에서 생성하고자 하는 데이터 유형을 출력 데이터라고 하고 이 출력 데이터를 생성하기 위해 사용되는 기반 데이터를 입력 데이터라고 한다. 회귀 분석에서는 독립 변수와 종속 변수라는 용어를 사용하며 머신 러닝에서는 일반적으로 feature와 target이라는 용어를 사용한다.

입력 데이터와 출력 데이터의 개념을 사용하여 예측 작업을 다시 설명하면 다음과 같다.

  • $X$와 $Y$의 관계 $f$를 파악 한다.
$$Y = f(X)$$
  • 현실적으로는 정확한 $f$를 구할 수 없으므로 $f$와 가장 유사한, 재현 가능한 $\hat{f}$을 구한다.
$$Y \approx \hat{f}(X)$$
  • $\hat{f}$를 가지고 있다면 $X$가 주어졌을 때 $Y$의 예측(추정) $\hat{Y} = \hat{f}(X)$를 구할 수 있다.
  • 확률론적으로 $\hat{f}$는
$$ \hat{f}(X) = \arg\max_{Y} P(Y | X) $$

예측은 입력 데이터와 출력 데이터 사이의 관계를 분석하고 분석한 관계를 이용하여 출력 데이터가 아직 없거나 혹은 가지고 있는 출력 여러가지 이유로 부정확하다고 생각될 경우 보다 합리적인 출력값을 추정하는 것이다. 따라서 입력 데이터와 출력 데이터의 관계에 대한 분석이 완료된 이후에는 출력 데이터가 필요 없어도 일단 관계를 분석하기 위해서는 입력 데이터와 출력 데이터가 모두 존재해야 한다.

데이터의 유형

예측 작업에서 생성하고자 하는 데이터 유형을 출력 데이터라고 하고 이 출력 데이터를 생성하기 위해 사용되는 기반 데이터를 입력 데이터라고 한다.

예측은 입력 데이터와 출력 데이터 사이의 관계를 분석하고 분석한 관계를 이용하여 출력 데이터가 아직 없거나 혹은 가지고 있는 출력 여러가지 이유로 부정확하다고 생각될 경우 보다 합리적인 출력값을 추정하는 것이다. 따라서 입력 데이터와 출력 데이터의 관계에 대한 분석이 완료된 이후에는 출력 데이터가 필요 없어도 일단 관계를 분석하기 위해서는 입력 데이터와 출력 데이터가 모두 존재해야 한다.

입력 데이터와 출력 데이터의 개념을 사용하여 예측 작업을 다시 설명하면 다음과 같다.

통계적 분석이나 머신 러닝 등의 데이터 분석에 사용되는 데이터의 유형은 다음 숫자 혹은 카테고리 값 중 하나이어야 한다.

  • 숫자 (number)
    • 크기/순서 비교 가능
    • 무한 집합
  • 카테고리값 (category)
    • 크기/순서 비교 불가
    • 유한 집합
    • Class
      • Binary Class
      • Multi Class

숫자와 카테고리 값의 차이점은 두 개의 데이터가 있을 때 이들의 크기나 혹은 순서를 비교할 수 있는가 없는가의 차이이다. 예를 들어 10kg과 23kg이라는 두 개의 무게는 23이 "크다"라고 크기를 비교하는 것이 가능하다. 그러나 "홍길동"과 "이순신"이라는 두 개의 카테고리 값은 크기를 비교할 수 없다.

일반적으로 카테고리 값은 가질 수 있는 경우의 수가 제한되어 있다. 이러한 경우의 수를 클래스(class)라고 부르는데 동전을 던진 결과와 같이 "앞면(head)" 혹은 "뒷면(tail)"처럼 두 가지 경우만 가능하면 이진 클래스(binary class)라고 한다. 주사위를 던져서 나온 숫자와 같이 세 개 이상의 경우가 가능하면 다중 클래스(multi class)라고 한다.

카테고리값처럼 비 연속적이지만 숫자처럼 비교 가능한 경우도 있을 수 있다. 예를 들어 학점이 "A", "B", "C", "D"와 같이 표시되는 경우는 비 연속적이고 기호로 표시되지만 크기 혹은 순서를 비교할 수 있다. 이러한 경우는 서수형(ordinal) 자료라고 하며 분석의 목표에 따라 숫자로 표기하기도 하고 일반적인 카테고리값으로 표기하기도 한다.

데이터의 변환 및 전처리

숫자가 아닌 이미지나 텍스트 정보는 분석에 목표에 따라 숫자나 카테고리 값으로 변환해야 한다. 이 때 해당하는 원본 정보를 손실 없이 그대로 숫자나 카테고리 값으로 바꿀 수도 있지만 대부분의 경우에는 분석에 필요한 핵심적인 정보만을 뽑아낸다. 이러한 과정은 데이터의 전처리(preprocessing)에 해당한다.


In [1]:
from sklearn.datasets import load_digits
digits = load_digits()
plt.imshow(digits.images[0], interpolation='nearest');
plt.grid(False)



In [2]:
digits.images[0]


Out[2]:
array([[  0.,   0.,   5.,  13.,   9.,   1.,   0.,   0.],
       [  0.,   0.,  13.,  15.,  10.,  15.,   5.,   0.],
       [  0.,   3.,  15.,   2.,   0.,  11.,   8.,   0.],
       [  0.,   4.,  12.,   0.,   0.,   8.,   8.,   0.],
       [  0.,   5.,   8.,   0.,   0.,   9.,   8.,   0.],
       [  0.,   4.,  11.,   0.,   1.,  12.,   7.,   0.],
       [  0.,   2.,  14.,   5.,  10.,  12.,   0.,   0.],
       [  0.,   0.,   6.,  13.,  10.,   0.,   0.,   0.]])

In [3]:
from sklearn.datasets import fetch_20newsgroups
news = fetch_20newsgroups()
print(news.data[0])


From: lerxst@wam.umd.edu (where's my thing)
Subject: WHAT car is this!?
Nntp-Posting-Host: rac3.wam.umd.edu
Organization: University of Maryland, College Park
Lines: 15

 I was wondering if anyone out there could enlighten me on this car I saw
the other day. It was a 2-door sports car, looked to be from the late 60s/
early 70s. It was called a Bricklin. The doors were really small. In addition,
the front bumper was separate from the rest of the body. This is 
all I know. If anyone can tellme a model name, engine specs, years
of production, where this car is made, history, or whatever info you
have on this funky looking car, please e-mail.

Thanks,
- IL
   ---- brought to you by your neighborhood Lerxst ----






In [4]:
from sklearn.feature_extraction.text import TfidfVectorizer
vec = TfidfVectorizer(stop_words="english").fit(news.data[:100])
data = vec.transform(news.data[:100])
data


Out[4]:
<100x6289 sparse matrix of type '<type 'numpy.float64'>'
	with 11711 stored elements in Compressed Sparse Row format>

In [5]:
plt.imshow(data.toarray()[:,:200], interpolation='nearest');


예측도 출력 데이터가 숫자인가 카테고리 값인가에 따라 회귀 분석(regression analysis)과 분류(classification)로 구분된다.

  • 회귀분석(regression)
    • 얻고자 하는 답 $Y$가 숫자
  • 분류 (classification)
    • 얻고자 하는 답 $Y$가 카테고리 값
X=Real X=Category
Y=Real Regression ANOVA
Y=Category Classification Classification

회귀 분석


In [6]:
from sklearn.datasets import load_boston
boston = load_boston()
print(boston.DESCR)


Boston House Prices dataset

Notes
------
Data Set Characteristics:  

    :Number of Instances: 506 

    :Number of Attributes: 13 numeric/categorical predictive
    
    :Median Value (attribute 14) is usually the target

    :Attribute Information (in order):
        - CRIM     per capita crime rate by town
        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
        - INDUS    proportion of non-retail business acres per town
        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
        - NOX      nitric oxides concentration (parts per 10 million)
        - RM       average number of rooms per dwelling
        - AGE      proportion of owner-occupied units built prior to 1940
        - DIS      weighted distances to five Boston employment centres
        - RAD      index of accessibility to radial highways
        - TAX      full-value property-tax rate per $10,000
        - PTRATIO  pupil-teacher ratio by town
        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
        - LSTAT    % lower status of the population
        - MEDV     Median value of owner-occupied homes in $1000's

    :Missing Attribute Values: None

    :Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
http://archive.ics.uci.edu/ml/datasets/Housing


This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980.   N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.   
     
**References**

   - Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
   - Quinlan,R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.
   - many more! (see http://archive.ics.uci.edu/ml/datasets/Housing)


In [7]:
df = pd.DataFrame(boston.data, columns=boston.feature_names)
df["MEDV"] = boston.target
df.tail()


Out[7]:
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT MEDV
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67 22.4
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08 20.6
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64 23.9
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48 22.0
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88 11.9

In [8]:
sns.pairplot(df[["MEDV", "RM", "AGE", "DIS"]]);



In [9]:
from sklearn.linear_model import LinearRegression
predicted = LinearRegression().fit(boston.data, boston.target).predict(boston.data)
plt.scatter(boston.target, predicted, c='r', s=20);
plt.xlabel("Target");
plt.ylabel("Predicted");


분류

  • Iris
setosa versicolor virginica

In [10]:
from sklearn.datasets import load_iris
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
sy = pd.Series(iris.target, dtype="category")
sy = sy.cat.rename_categories(iris.target_names)
df['species'] = sy
df.tail()


Out[10]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) species
145 6.7 3.0 5.2 2.3 virginica
146 6.3 2.5 5.0 1.9 virginica
147 6.5 3.0 5.2 2.0 virginica
148 6.2 3.4 5.4 2.3 virginica
149 5.9 3.0 5.1 1.8 virginica

In [11]:
sns.pairplot(df, hue="species");



In [12]:
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

X = iris.data[:, [2,3]]
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)
model = SVC(kernel="linear", C=1.0, random_state=0)
model.fit(X_train_std, y_train)

XX_min = X_train_std[:, 0].min() - 1; XX_max = X_train_std[:, 0].max() + 1;
YY_min = X_train_std[:, 1].min() - 1; YY_max = X_train_std[:, 1].max() + 1;
XX, YY = np.meshgrid(np.linspace(XX_min, XX_max, 1000), np.linspace(YY_min, YY_max, 1000))
ZZ = model.predict(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape)
cmap = mpl.colors.ListedColormap(sns.color_palette("Set2"))
plt.contourf(XX, YY, ZZ, cmap=cmap)
plt.scatter(X_train_std[y_train == 0, 0], X_train_std[y_train == 0, 1], c=cmap.colors[0], s=100)
plt.scatter(X_train_std[y_train == 1, 0], X_train_std[y_train == 1, 1], c=cmap.colors[2], s=100)
plt.scatter(X_train_std[y_train == 2, 0], X_train_std[y_train == 2, 1], c=cmap.colors[1], s=100)
plt.xlim(XX_min, XX_max);
plt.ylim(YY_min, YY_max);


클러스터링(Clustering)


In [15]:
from sklearn.cluster import DBSCAN
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import StandardScaler

X, labels_true = make_blobs(n_samples=750, centers=[[1, 1], [-1, -1], [1, -1]], cluster_std=0.4, random_state=0)
X = StandardScaler().fit_transform(X)
db = DBSCAN(eps=0.3, min_samples=10).fit(X)
n_clusters_ = len(set(db.labels_)) - (1 if -1 in db.labels_ else 0)
unique_labels = set(db.labels_)

f = plt.figure()
f.add_subplot(1,2,1)
plt.plot(X[:, 0], X[:, 1], 'o', markerfacecolor='k', markeredgecolor='k', markersize=10)
plt.title('Raw Data')
f.add_subplot(1,2,2)
colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
for k, col in zip(unique_labels, colors):
    if k == -1: col = 'k'
    class_member_mask = (db.labels_ == k)
    xy = X[class_member_mask]
    plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=col, markeredgecolor='k', markersize=10);
plt.title('Estimated number of clusters: %d' % n_clusters_);


모사(Approximation)


In [14]:
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances_argmin
from sklearn.datasets import load_sample_image
from sklearn.utils import shuffle

n_colors = 64
china = load_sample_image("china.jpg")
china = np.array(china, dtype=np.float64) / 255
w, h, d = original_shape = tuple(china.shape)
assert d == 3
image_array = np.reshape(china, (w * h, d))
image_array_sample = shuffle(image_array, random_state=0)[:1000]
kmeans = KMeans(n_clusters=n_colors, random_state=0).fit(image_array_sample)
labels = kmeans.predict(image_array)

def recreate_image(codebook, labels, w, h):
    d = codebook.shape[1]
    image = np.zeros((w, h, d))
    label_idx = 0
    for i in range(w):
        for j in range(h):
            image[i][j] = codebook[labels[label_idx]]
            label_idx += 1
    return image

print("{0:,} bytes -> {1:,} bytes : {2:5.2f}%".format(image_array.nbytes, labels.nbytes, float(labels.nbytes) / image_array.nbytes * 100.0))

f = plt.figure()
ax1 = f.add_subplot(1,2,1)
plt.axis('off')
plt.title('Original image (96,615 colors)')
ax1.imshow(china);
ax2 = f.add_subplot(1,2,2)
plt.axis('off')
plt.title('Quantized image (64 colors, K-Means)')
ax2.imshow(recreate_image(kmeans.cluster_centers_, labels, w, h));


6,558,720 bytes -> 1,093,120 bytes : 16.67%

데이터 분석에 대한 오해와 진실

  • 분석의 최대 성능은 방법론이 아닌 데이터 자체에 의존한다.
  • 사람이 이론적으로 분석할 수 없는 데이터는 어떤 머신러닝 알고리즘으로도 분석할 수 없다.

머신 러닝

  • 머신러닝이란 자동화된 데이터 분석
  • 머신러닝은 분석의 경제성과 효율성을 증가
  • 사람이 시간적, 경제적으로 할 수 없는 규모의 분석을 할 수 있다.